package gbench.appdemo.jdbcapp.accounting;

import org.junit.jupiter.api.Test;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import gbench.common.fs.FileSystem;
import gbench.common.tree.LittleTree;
import gbench.common.tree.LittleTree.IRecord;
import gbench.common.tree.LittleTree.Jdbc.*;
import gbench.common.tree.LittleTree.Jdbc;
import gbench.commonApp.data.DataApp;
import gbench.commonApp.data.DataMatrixApp;
import java.util.stream.*;
import static gbench.common.tree.LittleTree.*;
import static gbench.common.tree.LittleTree.IRecord.*;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import gbench.common.tree.LittleTree.SQL;

public class JunitAccounting extends DataMatrixApp{
    
    public class MyData extends DataApp{
        public List<IRecord> sqlquery(final String sql) {
            cfg.set("url", "jdbc:mysql://localhost:3306/eros?serverTimezone=GMT%2B8"); reload();
            return this.jdbc.sql2records(sql);
        }
    }
    
    /**
     * 会计分录的解析:记账法
     * @param namedsqls 记账模板
     * @return SqlInterceptor
     */
    public static SqlInterceptor<List<IRecord>> accountingmethod_interceptor(Map<String, String> namedsqls) {
        final var ree = REC(0,new LittleTree.ExpressionEvaluator());// 生成一个表达式计算器
        final var var_pattern = Pattern.compile("#{1}([a-z_][a-z0-9_]+)", Pattern.CASE_INSENSITIVE);// 变量模式
        final var expression_pattern = Pattern.compile("\\$\\s*\\{([^\\}]+)\\}", Pattern.CASE_INSENSITIVE);// 提出表达式项目:用 ${#amount*(1+0.17)}
        
        return (Method method,IRecord params,String sharppattern,Jdbc jdbc)->{
            
            final var sqlpattern = Jdbc.parseJdbcMethodSharpPattern (method,sharppattern,namedsqls);// 提取sql模版,sql 停留在方法对象中。
            // 表达式计算 & 表达式值的替换
            var sql = Jdbc.substitute(sqlpattern,var_pattern,params);// 把变量用params中的参数给与替换。
            var matcher = expression_pattern.matcher(sql);// 提取行数据中的所有表达式。
            while(matcher.find()) {// 提取所有的表达式pattern
                final var expression = matcher.group(1);
                final var value = ree.evalExpr(expression);// 计算表达式的值
                sql = matcher.replaceFirst(value.toString());
                matcher = expression_pattern.matcher(sql);// 重新计算匹配器
            }//while
            
            // 解析成会计分录
            return Arrays.stream(sql.split("\n+")).map(line->line.strip()).map( line-> {// 分解成字段单元
                final var ss= line.split("\\s{2,}");// 空2个以上的空格。分解语句格式。
                if(ss.length<3) {  System.err.println(MFT("{0} 格式错误",line)); return null;}
                final var drcr = "dr".equals(ss[0])?0:1; // 借贷双方
                final var name = ss[1];// 账号名称
                final var amount = Double.parseDouble(ss[2]);// 
                return REC("drcr",drcr,"account",name,"amount",amount);
            }).filter(e->e!=null).collect(Collectors.toList());
        };
    }
    
    /**
     * 记账数据库
     * @author gbench
     *
     */
    public interface FinanceDatabase extends DataApp.SimpleDatabase{
        
        @JdbcExecute({
            "drop table if exists {0}",
            "create table {0} (id int primary key auto_increment , "
            + "transcode int,"
            + "drcr int, account varchar(128), amount double,"
            + "create_time timestamp default now() )"})
        void createTable(String tableName);
    }
    
    /**
     * 记账法数据库
     * @author gbench
     *
     */
    public interface AccountingDatabase extends DataApp.SimpleDatabase{
        /**
         * 产品销售
         * @param amount 销售收入
         * @return 产品销售的会计分录
         */
        @JdbcQuery
        List<IRecord> revenue(double amount);
        
        /**
         * 结转成本
         * @param amount
         * @return
         */
        @JdbcQuery
        List<IRecord> cost(double amount);
    }
    
    /**
     * 自定义Jdbc
     * @author gbench
     */
    static class MyJdbc extends Jdbc{

        public MyJdbc(Map<?, ?> map) {
            super(map);
        }
        
        /**
         * 创建jdbc连接对象
         */
        public void init( final String driver, final String url, final String user, final String password){
            System.out.println("获取数据库连接！！！！！！");
            final var config = new HikariConfig();
            config.setJdbcUrl( url );
            config.setUsername( user);
            config.setPassword( password);
            config.addDataSourceProperty( "cachePrepStmts" , "true" );
            config.addDataSourceProperty( "prepStmtCacheSize" , "250" );
            config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" );
            final var ds = new HikariDataSource( config );
            this.supplierConn= () -> {
                try {
                    if(this.ds==null)this.ds = ds;
                    return ds.getConnection();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                return null;
            };
        }
        
        /**
         * 数据关闭。
         */
        public void close() {
            ds.close();
        }
        
        HikariDataSource ds = null;
        
    }
    
    @Test
    public void foo() {
        final var namedsqls = Jdbc.parse2namedsqls(FileSystem.utf8lines("accounting-method.sql",this.getClass()));
        var cfg = REC("url","jdbc:mysql://localhost:3306/myexchange?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=True",
            "driver","com.mysql.cj.jdbc.Driver","user","root","password","123456");
        //cfg.add("jdbcClass",MyJdbc.class.getName());// 增加自定义jdbcClass
        cfg = REC("_",new MyJdbc(cfg.toMap()));
        final var accountingDatabase = Jdbc.newInstance(AccountingDatabase.class,
            accountingmethod_interceptor(namedsqls));// 记账数据库
        
        final var tableName = "T_ACCENTRY";// 表名
        final var financeDatabase = Jdbc.newInstance(FinanceDatabase.class, cfg);
        final var jdbc = financeDatabase.getProxy().findOne(Jdbc.class);
        final var ai = new AtomicInteger(0);
        financeDatabase.createTable(tableName);// 创建数据表
        
        jdbc.withTransaction(sess->{// 开启数据库事务
            Stream.iterate(0, i->i+1).limit(100)
            .flatMap(i->{
                var l1 = accountingDatabase.revenue(RNDINT(10000));
                var l2 = accountingDatabase.cost(RNDINT(10000));
                l1.forEach(e->e.add("transcode", ai.get()));
                ai.getAndIncrement();
                l2.forEach(e->e.add("transcode", ai.get()));
                ai.getAndIncrement();
                return Stream.of(l1,l2).flatMap(e->e.stream());
            })
            .forEach(e->{
                final var sqlobj = new SQL(tableName,e);
                final var sql = sqlobj.insert();
                try {sess.sqlexecute(sql);}
                catch (SQLException e1) {e1.printStackTrace(); }
                System.out.println(sql);
            });// 
        });
        
        if(jdbc instanceof MyJdbc)((MyJdbc)jdbc).close();// 圈闭数据库连接池
       
    }
}
